﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;


/*************************************************
 * This program is free software: you can redistribute it 
 * and/or modify it but please leave header.
 * 
 * Created by NeoModulus
 * **********************************************/
namespace QuicksandCRUD
{
   
    public sealed class InMemoryRepositorySingleton
    {
        private static volatile InMemoryRepositorySingleton instance;
        private static readonly object syncRoot = new Object();

        private readonly Dictionary<string, ICollection<ResourceBase>> _inMemoryRepo;

        private InMemoryRepositorySingleton()
        {
            _inMemoryRepo = new Dictionary<string, ICollection<ResourceBase>>();
        }

        public static InMemoryRepositorySingleton Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                        {
                            instance = new InMemoryRepositorySingleton();
                        }
                    }
                }

                return instance;
            }
        }

        private static IEnumerable<string> GetResourceFiles(string resourceLocation)
        {
            string RootResourceFile = string.Empty;
            if (ConfigurationManager.AppSettings["RootResourceFile"] != null)
            {
                RootResourceFile = ConfigurationManager.AppSettings["RootResourceFile"];
            }

            if (!string.IsNullOrEmpty(RootResourceFile))
            {
                if (Directory.Exists(RootResourceFile + resourceLocation))
                {
                    return Directory.GetFiles(RootResourceFile + resourceLocation, "*.xml");
                }
            }

            return new string[0];
        }

        private void Store(string key, ICollection<ResourceBase> objects)
        {
            if (!_inMemoryRepo.ContainsKey(key))
            {
                _inMemoryRepo.Add(key, objects);
            }
            else
            {
                _inMemoryRepo[key] = objects;
            }
        }

        private void Update(ResourceBase objectToUpdate)
        {
            if (_inMemoryRepo.ContainsKey(objectToUpdate.RESOURCEKEY))
            {
                if (_inMemoryRepo[objectToUpdate.RESOURCEKEY].FirstOrDefault(obj => obj.ResourceID == objectToUpdate.ResourceID) != null)
                {
                    _inMemoryRepo[objectToUpdate.RESOURCEKEY].Remove(_inMemoryRepo[objectToUpdate.RESOURCEKEY].FirstOrDefault(obj => obj.ResourceID == objectToUpdate.ResourceID));
                }

                _inMemoryRepo[objectToUpdate.RESOURCEKEY].Add(objectToUpdate);
            }
            else
            {
                ICollection<ResourceBase> objects = new List<ResourceBase>();
                objects.Add(objectToUpdate);
                _inMemoryRepo.Add(objectToUpdate.RESOURCEKEY, objects);
            }
        }

        public void Delete(ResourceBase objectToDelete)
        {

            string RootResourceFile = string.Empty;
            if (ConfigurationManager.AppSettings["RootResourceFile"] != null)
            {
                RootResourceFile = ConfigurationManager.AppSettings["RootResourceFile"];
            }


            if (_inMemoryRepo.ContainsKey(objectToDelete.RESOURCEKEY))
            {
                if (_inMemoryRepo[objectToDelete.RESOURCEKEY].FirstOrDefault(obj => obj.ResourceID == objectToDelete.ResourceID) != null)
                {
                    _inMemoryRepo[objectToDelete.RESOURCEKEY].Remove(_inMemoryRepo[objectToDelete.RESOURCEKEY].FirstOrDefault(obj => obj.ResourceID == objectToDelete.ResourceID));
                }
            }

            if (!string.IsNullOrEmpty(RootResourceFile))
            {
                if (File.Exists(RootResourceFile + objectToDelete.RESOURCELOCATION + objectToDelete.ResourceID + @".xml"))
                {
                   File.Delete(RootResourceFile + objectToDelete.RESOURCELOCATION + objectToDelete.ResourceID + @".xml");
                   }
            }
        }

        public ICollection<ResourceBase> GetAll(ResourceBase ofTypeObject)
        {
            if (_inMemoryRepo.ContainsKey(ofTypeObject.RESOURCEKEY))
            {
                return _inMemoryRepo[ofTypeObject.RESOURCEKEY];
            }

            //Not in memory so load from disk
            IEnumerable<string> filesToLoad = GetResourceFiles(ofTypeObject.RESOURCELOCATION);

            List<ResourceBase> objects = new List<ResourceBase>();

            foreach(string filename in filesToLoad)
            {
                XmlSerializer xs = new XmlSerializer(ofTypeObject.RESOURCETYPE);
                MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(File.ReadAllText(filename)));
                objects.Add((ResourceBase)xs.Deserialize(memoryStream));
            }

            Store(ofTypeObject.RESOURCEKEY, objects);

            return objects;
        }

        public ResourceBase GetByID(Guid id, ResourceBase ofTypeObject)
        {
            ICollection<ResourceBase> objects = GetAll(ofTypeObject);

            return objects.FirstOrDefault(obj => obj.ResourceID == id);
        }

        public void Save(ResourceBase objectToSave)
        {
            string RootResourceFile = string.Empty;
            if (ConfigurationManager.AppSettings["RootResourceFile"] != null)
            {
                RootResourceFile = ConfigurationManager.AppSettings["RootResourceFile"];
            }

            if (!string.IsNullOrEmpty(RootResourceFile))
            {
                if (!Directory.Exists(RootResourceFile + objectToSave.RESOURCELOCATION))
                {
                    Directory.CreateDirectory(RootResourceFile + objectToSave.RESOURCELOCATION);
                }

                XmlSerializer xmlSerializer = new XmlSerializer(objectToSave.RESOURCETYPE);

                try
                {

                    string xmlString = null;
                    MemoryStream memoryStream = new MemoryStream();
                    XmlSerializer xs = new XmlSerializer(objectToSave.RESOURCETYPE);
                    XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
                    xs.Serialize(xmlTextWriter, objectToSave);
                    memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
                    xmlString = UTF8ByteArrayToString(memoryStream.ToArray());
                    File.WriteAllText(RootResourceFile + objectToSave.RESOURCELOCATION + objectToSave.ResourceID + @".xml", xmlString);
                }
                catch (Exception ex)
                {

                }
            }

            Update(objectToSave);
        }

        private static string UTF8ByteArrayToString(byte[] characters)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            string constructedString = encoding.GetString(characters);
            return (constructedString);
        }

        private static Byte[] StringToUTF8ByteArray(string pXmlString)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            byte[] byteArray = encoding.GetBytes(pXmlString);
            return byteArray;
        }

    }
}
